package com.xishu.service;

import cn.hutool.core.convert.Convert;
import com.xishu.bo.CashAndCount;
import com.xishu.bo.CloseReport;
import com.xishu.bo.shop.EmailPayType;
import com.xishu.client.EmailClient;
import com.xishu.config.Config;
import com.xishu.config.Constant;
import com.xishu.dao.jpa.FoodTableDao;
import com.xishu.dao.jpa.ItemDao;
import com.xishu.dao.jpa.OrderDao;
import com.xishu.entity.AbstractEntity;
import com.xishu.entity.Company;
import com.xishu.entity.User;
import com.xishu.entity.area.Area;
import com.xishu.entity.feedback.Feedback;
import com.xishu.entity.hardware.Printer;
import com.xishu.entity.order.*;
import com.xishu.entity.plat.UserShopMapping;
import com.xishu.entity.shop.*;
import com.xishu.exception.ResponseStatusException;
import com.xishu.response.ResponseStatus;
import com.xishu.response.VerifyUtil;
import com.xishu.service.order.base.OrderService;
import com.xishu.service.print.PrinterService;
import com.xishu.socket.WebSocketService;
import com.xishu.util.*;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.ThreadContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static com.xishu.config.Constant.*;

public class ShopService implements Tools {
    private static ShopService instance = new ShopService();
    private static Logger logger = LoggerFactory.getLogger(ShopService.class);
    private Map<String, String> usState = new HashMap<>();
    private CommonService commonService = CommonService.getInstance();

    private ShopService() {
        usState.put("Alabama", "AL");
        usState.put("Alaska", "AK");
        usState.put("Arizona", "AZ");
        usState.put("Arkansas", "AR");
        usState.put("California", "CA");
        usState.put("Colorado", "CO");
        usState.put("Connecticut", "CT");
        usState.put("Delaware", "DE");
        usState.put("District of Columbia", "DC");
        usState.put("Florida", "FL");
        usState.put("Georgia", "GA");
        usState.put("Hawaii", "HI");
        usState.put("Idaho", "ID");
        usState.put("Illinois", "IL");
        usState.put("Indiana", "IN");
        usState.put("Iowa", "IA");
        usState.put("Kansas", "KS");
        usState.put("Kentucky", "KY");
        usState.put("Louisiana", "LA");
        usState.put("Maine", "ME");
        usState.put("Maryland", "MD");
        usState.put("Massachusetts", "MA");
        usState.put("Michigan", "MI");
        usState.put("Minnesota", "MN");
        usState.put("Mississippi", "MS");
        usState.put("Missouri", "MO");
        usState.put("Montana", "MT");
        usState.put("Nebraska", "NE");
        usState.put("Nevada", "NV");
        usState.put("New Hampshire", "NH");
        usState.put("New Jeresy", "NJ");
        usState.put("New Mexico", "NM");
        usState.put("New York", "NY");
        usState.put("North Carolina", "NC");
        usState.put("North Dakota", "ND");
        usState.put("Ohio", "OH");
        usState.put("Oklahoma", "OK");
        usState.put("Oregon", "OR");
        usState.put("Pennsylvania", "PA");
        usState.put("Rhode Island", "RI");
        usState.put("SouthCarolina", "SC");
        usState.put("South Dakota", "SD");
        usState.put("Tennessee", "TN");
        usState.put("Texas", "TX");
        usState.put("Utah", "UT");
        usState.put("Vermont", "VT");
        usState.put("Virginia", "VA");
        usState.put("Washington", "WA");
        usState.put("West Virginia", "WV");
        usState.put("Wisconsin", "WI");
        usState.put("Wyoming", "WY");
    }

    public static ShopService getInstance() {
        return instance;
    }

    /**
     * 通过ID查询已经上线的shop
     *
     * @param shopId
     * @return
     */
    public Shop findShopById(Long shopId) throws Exception {
        if (shopId == null) {
            logger.info("do not have shop id");
            return null;
        }

        long startTime = System.currentTimeMillis();
        CommonService commonService = CommonService.getInstance();
        Shop shop = new Shop();
        shop.setId(shopId);
        Shop shopEntity = commonService.findEntity(shop, true);
        logger.info("find the shop id {} cost time {}", shopId, System.currentTimeMillis() - startTime);

        setShopConfigDefaultValue(shopEntity);
        setFoodPandaValue(shopEntity);
        logger.info("set config cost time  {}", System.currentTimeMillis() - startTime);

        return shopEntity;
    }

    /**
     * 查询分店列表
     *
     * @param idList
     * @return
     */
    public List<Shop> findShopByList(List<Long> idList) {
        Shop shop = new Shop();
        shop.setEntity(true);
        shop.setIds(idList);
        return commonService.searchAll(shop);
    }

    /**
     * 设置分店的默认配置
     *
     * @param shopEntity
     */
    public void setShopConfigDefaultValue(Shop shopEntity) {
        //已经处理过了默认值，不再处理
        if (getBoolean(shopEntity.getDefaultValueConfig())) {
            return;
        }

        shopEntity.setDefaultValueConfig(true);

        if (shopEntity.getShopConfig() == null) {
            ShopConfig shopConfig = new ShopConfig();
            shopEntity.setShopConfig(shopConfig);
            commonService.dealDefaultValueWithAnnotation(shopConfig);
            commonService.setDefaultValue(shopConfig);
        } else {
            commonService.dealDefaultValueWithAnnotation(shopEntity.getShopConfig());
            commonService.setDefaultValue(shopEntity.getShopConfig());
        }
    }

    /**
     * 处理foodpanda相关的配置
     *
     * @param shop
     */
    public void setFoodPandaValue(Shop shop) {
        if (shop.getFoodPandaConfig() != null) {
            return;
        }

        shop.setFoodPandaConfig(new FoodPandaConfig());
    }


    /**
     * 查询所有的分店列表
     *
     * @return
     */
    public List<Shop> findAllShop() throws Exception {
        List<Shop> shopList = CommonService.getInstance().searchAll(Shop.class);
        for (Shop shop : shopList) {
            if (shop.getShopConfig() == null) {
                ShopConfig shopConfig = new ShopConfig();
                shop.setShopConfig(shopConfig);
            }
        }

        return shopList;
    }

    /**
     * 查询分店
     *
     * @param shopId
     * @return
     */
    public Optional<Shop> findShopOptional(Long shopId) {
        Shop shop = new Shop();
        shop.setId(shopId);
        Optional<Shop> shopOptional = commonService.searchOneOptional(shop);

        if (shopOptional.isPresent()) {
            if (shopOptional.get().getShopConfig() == null) {
                shopOptional.get().setShopConfig(new ShopConfig());
            }
        }

        return shopOptional;
    }

    /**
     * 通过公司ID查询商铺列表
     *
     * @param companyId
     * @return
     */
    public List<Shop> findShopListByCompanyId(Long companyId) {
        Shop shop = new Shop();
        shop.setCompanyId(companyId);
        shop.setEntity(true);
        List<Shop> shopList = commonService.searchAll(shop);
        shopList.forEach(s -> {
            setShopConfigDefaultValue(s);
            setFoodPandaValue(s);
        });

        return shopList;
    }

    /**
     * 创建默认的模式,有新的模式的时候，再去做额外的处理
     *
     * @param shopId
     */
    public void createRunModel(Long shopId) {
        CommonService commonService = CommonService.getInstance();

        //依次创建运营模式
        for (int i = 0; i < 6; i++) {
            RunModel runModel = new RunModel();
            runModel.setShopId(shopId);
            runModel.setModelType(i);

            RunModel runModelInDb = commonService.searchOne(runModel);

            //数据库里面的运营模式
            if (runModelInDb != null) {
                logger.info("shop {} run model {} exist, continue", shopId, i);
                continue;
            }

            //快餐模式默认开启
            if (i == Constant.RUN_MODEL_TYPE_QUICK_MODEL) {
                runModel.setEnable(true);
            } else {
                runModel.setEnable(false);
            }

            try {
                commonService.createObject(runModel);
            } catch (Exception e) {
                logger.error("e", e);
            }
        }
    }

    /**
     * @param shopId
     */
    public void createShopFlagWithCloseReport(Long shopId) throws Exception {
        VerifyUtil.verify(() -> shopId != null);
        //判断当前分店是否已经打印了，如果没有打印则添国打印标记
        ShopFlag shopFlag = new ShopFlag();
        shopFlag.setShopId(shopId);
        shopFlag.setFlagTime(DateUtil.formatDayDate(System.currentTimeMillis()));
        CommonService commonService = CommonService.getInstance();
        ShopFlag shopFlagInDb = commonService.searchOne(shopFlag);
        if (shopFlagInDb != null) {
            if (getBoolean(shopFlagInDb.getCloseReportPrinted())) {
                logger.info("shop {} already printed, not set flag again", shopId);
                return;
            } else {
                shopFlagInDb.setCloseReportPrinted(true);
                commonService.directSave(shopFlagInDb);
            }
        } else {
            logger.info("will create flag object for {}", shopId);
            shopFlag.setCloseReportPrinted(true);
            commonService.createObject(shopFlag);
        }
    }

    /**
     * 搜索触摸屏设备
     *
     * @param touchDeviceUUID
     * @return
     */
    public List<TouchDevice> findTouchDevice(String touchDeviceUUID) {
        TouchDevice device = new TouchDevice();
        device.setUuid(touchDeviceUUID);
        return commonService.searchAll(device);
    }

    /**
     * 查询商铺下面所有的触摸屏
     *
     * @param shopId
     * @return
     */
    public List<TouchDevice> findTouchDeviceList(Long shopId) throws Exception {
        return findEntityListByShopId(shopId, TouchDevice.class);
    }

    /**
     * 查询分店的所有场景
     *
     * @param shopId
     * @return
     */
    public List<Scene> findSceneList(Long shopId) throws Exception {
        return findEntityListByShopId(shopId, Scene.class);
    }

    /**
     * 查询餐桌配置
     *
     * @param shopId
     * @return
     */
    public List<FoodTableConfig> findFoodTableConfig(Long shopId) throws Exception {
        return findEntityListByShopId(shopId, FoodTableConfig.class);
    }

    /**
     * 查询实体列表
     *
     * @param shopId
     * @param tClass
     * @param <T>
     * @return
     * @throws Exception
     */
    public <T extends AbstractEntity> List<T> findEntityListByShopId(Long shopId, Class<T> tClass) throws Exception {
        T instance = tClass.newInstance();
        Field shopIdField = ClassUtil.getInstance().getField(tClass, "shopId");
        if (shopIdField == null) {
            logger.info("can not find the shopId field for class {}", tClass.getName());
            return new ArrayList<>();
        }

        shopIdField.set(instance, shopId);
        instance.setEntity(true);
        List<T> list = commonService.searchAll(instance);
        return list;
    }

    /**
     * 删除实体
     *
     * @param shopId
     * @param tClass
     * @param <T>
     * @throws Exception
     */
    public <T extends AbstractEntity> void deleteEntity(Long shopId, Class<T> tClass) throws Exception {
        T instance = (T) tClass.newInstance();
        Field shopIdField = ClassUtil.getInstance().getField(tClass, "shopId");
        if (shopIdField == null) {
            logger.info("can not find the shopId field for class {}", tClass.getName());
            return;
        }

        shopIdField.set(instance, shopId);
        instance.setEntity(true);
        List<T> list = commonService.searchAll(instance);
        for (T t : list) {
            t.setFlagDelete(true);
            commonService.save(t);
        }
    }

    /**
     * 设备是否在线
     *
     * @param touchDevice
     * @return
     */
    public boolean isTouchDeviceOnline(TouchDevice touchDevice) {
        return System.currentTimeMillis() - touchDevice.getUpdateTime() < Config.getInstance().touchDeviceExpireTime() * DateUtil.ONE_SECONDS;
    }

    /**
     * 查询所有的餐桌
     *
     * @param shopId
     * @return
     */
    public List<FoodTable> findFoodTable(Long shopId) throws Exception {
        return findEntityListByShopId(shopId, FoodTable.class);
    }

    /**
     * 获取带ID的商铺地址
     *
     * @param shop
     * @return
     */
    public String getShopAddress(Shop shop) {
        if (isNotEmpty(shop.getShopAddress_zh())) {
            return shop.getShopAddress_zh();
        } else if (isNotEmpty(shop.getShopAddress_en())) {
            return shop.getShopAddress_en();
        } else {
            return shop.getShopAddress();
        }
    }

    /**
     * 查询地址列表
     *
     * @param areaIds
     * @return
     */
    public List<Area> findAreaList(List<Long> areaIds) {
        Area area = new Area();
        area.setIds(areaIds);
        return commonService.searchAll(area);
    }

    /**
     * 查询所有的品类
     *
     * @param shopId
     * @return
     * @throws Exception
     */
    public List<Catalog> findCatalog(Long shopId) throws Exception {
        return findEntityListByShopId(shopId, Catalog.class);
    }

    /**
     * 格式化商铺地址
     *
     * @param shop
     */
    public void formatShopAddress(Shop shop) {
        //添加中英文地址
        String shopAddress = getShopAddress(shop);
        try {
            logger.info("the shop address is {}", shopAddress);
            if (isNotEmpty(shopAddress)) {
                String[] areaIdStringList = shopAddress.split(",");
                List<Long> areaIds = Arrays.stream(areaIdStringList).map(p -> Long.parseLong(p)).collect(Collectors.toList());
                List<Area> areaList = findAreaList(areaIds);
                String address_zh = StringUtils.join(areaList.stream().map(p -> p.getName_zh()).collect(Collectors.toList()), " ");
                String address_en = StringUtils.join(areaList.stream().map(p -> p.getName_en()).collect(Collectors.toList()), " ");
                shop.setShowAddressShow_zh(address_zh);
                shop.setShowAddressShow_en(address_en);
                logger.info("shop is {}", shop);
            }
        } catch (Exception e) {
            logger.error("shop data is old");
            shop.setShowAddressShow_zh(shop.getShopAddress_zh());
            shop.setShowAddressShow_en(shop.getShopAddress_en());
            logger.info("shop is {}", shop);
        }
    }

    /**
     * 设置支付标记
     *
     * @param shop
     */
    public void setPayFlag(Shop shop) {
        if (shop == null) {
            logger.info("shop is empty");
            return;
        }

        //只配置B端
        if (!getBoolean(shop.getQfConfigCEnd())) {
            if (getBoolean(shop.getQfEnable()) && isNotEmpty(shop.getQfCode()) && isNotEmpty(shop.getQfSid()) && isNotEmpty(shop.getQfKey())) {
                shop.setQfComplete(true);
            }
        } else {
            //以端也要配置
            if (getBoolean(shop.getQfEnable()) && isNotEmpty(shop.getQfCode()) && isNotEmpty(shop.getQfSid()) && isNotEmpty(shop.getQfKey()) && isNotEmpty(shop.getQfWeixinAppId()) && isNotEmpty(shop.getQfAlipayAppId())) {
                shop.setQfComplete(true);
            }
        }

        if (getBoolean(shop.getStripeEnable()) && isNotEmpty(shop.getStripeAk()) && isNotEmpty(shop.getStripeSk()) && isNotEmpty(shop.getStripeWebHookSk())) {
            shop.setStripeComplete(true);
        }
    }

    /**
     * 校验stripe配置是否正确
     *
     * @param shop
     */
    public void checkStripeConfig(Shop shop) throws ResponseStatusException {
        if (Config.getInstance().isAliyun()) {
            return;
        }

        if (!Config.getInstance().isCheckStripeConfigCheck()) {
            logger.info("do not check the stripe config");
            return;
        }

        if (isNotEmpty(shop.getStripeAk())) {
            boolean testModule = shop.getStripeAk().contains("_test_");
            VerifyUtil.verify(() -> !testModule, ResponseStatus.PLEASE_CONFIG_STRIPE_LIVE_MODE);
        }

        if (isNotEmpty(shop.getStripeSk())) {
            boolean testModule = AES.decrypt(shop.getStripeSk()).contains("_test_");
            VerifyUtil.verify(() -> !testModule, ResponseStatus.PLEASE_CONFIG_STRIPE_LIVE_MODE);
        }
    }

    /**
     * 清除订单号
     *
     * @throws Exception
     */
    public void cleanOrderNo(Long shopId) throws Exception {
        OrderNoService.getInstance().resetOrderNo(shopId);
    }

    /**
     * 关闭餐厅
     *
     * @param shopInDb
     * @throws Exception
     */
    public void closeShop(Shop shopInDb, boolean zh) throws Exception {
        try {
            Long shopId = shopInDb.getId();
            logger.info("begin to set shop {} status to {}", shopInDb.getId(), SHOP_STATUS_CLOSING);
            shopInDb.setStatus(SHOP_STATUS_CLOSING);
            commonService.save(shopInDb);
            //强制关闭，或者没有部分付款的订单
            LineUpService.getInstance().closeAllLineUp(shopId);
            OrderService.getInstance().allOrderCloseQrScanShow(shopId);
            logger.info("closed all order");

            //真删，以避免餐桌过多
            FoodTableDao foodTableDao = BeanUtil.getBean(FoodTableDao.class);
            foodTableDao.flagDeleteNonTemplateFoodTable(shopId);
            logger.info("delete all non-template food tables");

            //将所有的餐桌模板状态，以及固定餐桌，均设置成空闲状态，
            foodTableDao.updateTemplateFoodTable(shopId, Constant.FOOD_TABLE_STATUS_EMPTY, true);
            logger.info("update template  food tables status");

            OrderDao orderDao = BeanUtil.getBean(OrderDao.class);
            Long openTime = shopInDb.getOpenTime();

            if (openTime == null) {
                openTime = 0l;
            }

            long endTime = System.currentTimeMillis();
            logger.info("close report start create time {} - {}, end create time {} - {}, shopId {}", openTime, DateUtil.formatDate(openTime),
                    endTime, DateUtil.formatDate(endTime),
                    shopId);
            orderDao.cleanOrderBusinessEndShow(shopId, openTime, System.currentTimeMillis());

            //将没有完成的订单关闭
            orderDao.closeOrder(shopId, openTime, System.currentTimeMillis());
            logger.info("close all order");

            //清空锁定量
            ItemDao itemDao = BeanUtil.getBean(ItemDao.class);
            itemDao.cleanLockCount(shopId);

            //清楚緩存中分店的菜品的緩存
            List<Item> srcItemList = ItemService.getInstance().findItemByShopId(shopId);
            for (Item item : srcItemList) {
                CommonService.getInstance().deleteFromCache(item);
            }

            //查询出订单号是0的订单，再处理成非零的订单号
            List<Order> fixOrderList = orderDao.findFixOrder(shopId, openTime, System.currentTimeMillis());
            for (Order order : fixOrderList) {
                commonService.dealFieldMapping(order, false);
                OrderService.getInstance().resetFixOrder(order);
            }

            cleanOrderNo(shopId);

            //新生成闭店序列号，写到每一个订单里面
            int timeFix = OrderNoService.getInstance().getTimeFix(shopId);

            //生成这个周期的时间序列号
            List<Order> orderList = orderDao.findTimeOrderCloseShop(shopId, getLong(shopInDb.getOpenTime()), System.currentTimeMillis(), true, false);
            logger.info("orderList size is {}", orderList.size());
            resetTimeNo(orderList, timeFix);
            logger.info("reset all time no end");

            //将闭店序列号写到分店里面
            shopInDb.setTimeFix(timeFix);
            commonService.save(shopInDb);

            //关店的时候，统一处理报告
            //添加分析报告处理,只处理已支付，未退款的订单
            //已支付，退款，再支付，算已支付的订单
            //已支付，全部退款，算退款的订单

            List<Order> payedOrderList = orderDao.findTimeOrder(shopId, getLong(shopInDb.getOpenTime()), System.currentTimeMillis(), true, true, false);
            List<Order> parentOrderList = payedOrderList.stream().filter(p -> getBoolean(p.getParentOrder())).collect(Collectors.toList());
            for (Order order : parentOrderList) {
                //没有支付
                if (!getBoolean(order.getPayedOnce())) {
                    continue;
                }

                if (getBoolean(order.getReturnCash())) {
                    continue;
                }

                orderReport(shopId, order.getId());
            }

            //失效订单处理
            List<Order> expiredOrderList = payedOrderList.stream().filter(p -> getBoolean(p.getExpiredOrder())).collect(Collectors.toList());
            for (Order order : expiredOrderList) {
                addSubOrderToReport(order);
                //将失效订单修改成父订单，以方便流水查询
                order.setParentOrder(true);
                commonService.save(order);
            }

            //删除这些时间没有支付的子订单
            OrderService.getInstance().deleteUnpaySubOrder(shopId, shopInDb.getOpenTime(), System.currentTimeMillis());
            logger.info("delete all unpaid orders");

            //如果存在落单审核，全部删除掉
            OrderService.getInstance().deleteSendToKitchen(shopId);

            //发送socket 消息通知
            WebSocketService.getInstance().sendKitchenOrderAudit(shopId, 0);

            //查询所有的商铺经理，发送闭店报告
            UserShopMapping userShopMapping = new UserShopMapping();
            userShopMapping.setEntity(true);
            userShopMapping.setShopId(shopInDb.getId());
            String reportContent = generateSendCloseReport(shopInDb, zh);

            List<UserShopMapping> userShopMappingList = commonService.searchAll(userShopMapping);
            Set<String> sendEmailSet = new HashSet<>();
            for (UserShopMapping shopMapping : userShopMappingList) {
                Long userId = shopMapping.getUserId();
                Optional<User> userOptional = commonService.searchOneOptional(userId, User.class);
                if (!userOptional.isPresent()) {
                    continue;
                }

                User user = userOptional.get();
                String email = UserService.getInstance().userEmail(user);
                if (isNotEmpty(email)) {
                    sendEmailSet.add(email);
                }
            }

            if (Config.getInstance().isAliyun()) {
                //国内环境
                logger.info("content is {}", reportContent);
                File pdfFile = PdfUtil.htmlToPdf(reportContent);
                EmailClient.getInstance().sendEmail("2681571047@qq.com", "今日營業報告/Today Report", reportContent, pdfFile);
            } else {
                //开始发送
                for (String email : sendEmailSet) {
                    logger.info("content is {}", reportContent);
                    File pdfFile = PdfUtil.htmlToPdf(reportContent);
                    EmailClient.getInstance().sendEmail(email, "今日營業報告/Today Report", reportContent, pdfFile);
                }
            }

        } catch (Exception e) {
            logger.error("e", e);
        } finally {
            //操作完成了
            shopInDb.setStatus(SHOP_STATUS_NORMAL);
            logger.info("deal end");
            commonService.save(shopInDb);
        }
    }


    /**
     * 发送闭店报告
     * 先把数据准备好，然后把HTML写好
     * 最后把HTML发送到各个邮箱
     *
     * @param shopInDb
     */
    public String generateSendCloseReport(Shop shopInDb, boolean zh) throws Exception {
        Config config = Config.getInstance();
        Long shopId = shopInDb.getId();
        ReportService reportService = ReportService.getInstance();

        CloseReport closeReport = CloseReportService.getInstance().generateCloseReport(shopId);

        Long companyId = shopInDb.getCompanyId();
        Company company = CompanyService.getInstance().findCompanyById(companyId);

        String shopName = zh ? shopInDb.getName_zh() : shopInDb.getName_en();
        String companyName = zh ? company.getName_zh() : company.getName_en();

        String address_title = zh ? "地址：" : "Address:";
        //地区 + 详细地址
        String address = null;

        if (config.isHkVersion()) {
            address = LangService.getInstance().getShopAddress(shopInDb, zh);
        } else {
            //美国版本
            address = shopInDb.getUsAddress();
        }

        String phone_title = zh ? "電話：" : "Phone:";
        String phone = shopInDb.getPhone();
        String date_title = zh ? "日期：" : "Date:";
        String date = DateUtil.formatDateMin(shopInDb.getOpenTime()) + " - " + DateUtil.formatDateMin(System.currentTimeMillis());

        //以下是开始组装数据
        String payType_title = zh ? "支付方式" : "DEPT";
        String tangshi_title = zh ? "堂食" : "HERE";
        String takeout_title = zh ? "外賣" : "TAKE OUT";
        String total_title = zh ? "總額" : "TOTAL";
        List<EmailPayType> emailPayTypeList = addPayTypeCash(zh, shopId);

        //计算总的销售额，按照销售场景来处理
        String sale_total_title = null;

        if (config.isUsVersion()) {
            sale_total_title = zh ? "總計" : "Total";
        } else {
            sale_total_title = zh ? "營業額" : "Total";
        }


        String tangshi_total = BigDecimalUtil.format(BigDecimal.valueOf(showMoneyDouble(emailPayTypeList.stream().mapToDouble(p -> stringToDouble(p.getTangshi())).sum())));
        String take_out_total = BigDecimalUtil.format(BigDecimal.valueOf(showMoneyDouble(emailPayTypeList.stream().mapToDouble(p -> stringToDouble(p.getTakeout())).sum())));
        String total_double = BigDecimalUtil.format(BigDecimal.valueOf(showMoneyDouble(Convert.toDouble(tangshi_total) + Convert.toDouble(take_out_total))));

        //服务费，小費，消費稅，餐盒费，外卖折扣，优惠卷
        String service_fee_title = zh ? "服務費" : "Service Charge";
        String tips_fee_title = zh ? "小費" : "Tips";
        String consumption_fee_title = zh ? "消費稅" : "Sales Tax";
        String packing_fee_title = zh ? "餐盒費" : "Packing Fee";
        String take_out_discount_title = zh ? "外賣折扣" : "Take Out Discount";
        String ticket_title = zh ? "優惠券" : "Coupon";

        String serviceFee = BigDecimalUtil.format(BigDecimal.valueOf(showMoneyDouble(ReportService.getInstance().sumServiceFee(shopId))));
        String packageFee = BigDecimalUtil.format(BigDecimal.valueOf(showMoneyDouble(ReportService.getInstance().sumPackageFee(shopId))));
        String takeOutDiscountFee = "-" + BigDecimalUtil.format(BigDecimal.valueOf(showMoneyDouble(ReportService.getInstance().sumTakeOutDiscount(shopId))));
        String discountFee = BigDecimalUtil.format(BigDecimal.valueOf(showMoneyDouble(ReportService.getInstance().sumDiscountFee(shopId))));

        EmailPayType service_fee_pay_type = new EmailPayType();
        service_fee_pay_type.setPayType(service_fee_title);
        service_fee_pay_type.setTotal(String.valueOf(serviceFee));

        //小費
        EmailPayType tips_pay_type = new EmailPayType();
        tips_pay_type.setPayType(tips_fee_title);
        tips_pay_type.setTotal(String.valueOf(showMoneyDouble(ReportService.getInstance().sumTipsFee(shopId))));

        //消費稅
        EmailPayType sale_tax_pay_type = new EmailPayType();
        sale_tax_pay_type.setPayType(consumption_fee_title);
        sale_tax_pay_type.setTotal(String.valueOf(showMoneyDouble(ReportService.getInstance().sumConsumptionTax(shopId))));

        EmailPayType package_fee_pay_type = new EmailPayType();
        package_fee_pay_type.setPayType(packing_fee_title);
        package_fee_pay_type.setTotal(String.valueOf(packageFee));

        EmailPayType take_out_fee_pay_type = new EmailPayType();
        take_out_fee_pay_type.setPayType(take_out_discount_title);
        take_out_fee_pay_type.setTotal(takeOutDiscountFee);

        EmailPayType ticket_fee_pay_type = new EmailPayType();
        ticket_fee_pay_type.setPayType(ticket_title);
        ticket_fee_pay_type.setTotal("-" + discountFee);

        //未付款，已退款
        String unpaid_title = zh ? "未支付" : "Unpaid";
        String tangShiUnpaid = BigDecimalUtil.format(BigDecimal.valueOf(showMoneyDouble(ReportService.getInstance().countSendToKitchenUnpaidForTangShi(shopId))));
        String takeOutUnpaid = BigDecimalUtil.format(BigDecimal.valueOf(showMoneyDouble(ReportService.getInstance().sumSendToKitchenUnpaidForTakeOut(shopId))));
        String totalUnpaid = BigDecimalUtil.format(BigDecimal.valueOf(showMoneyDouble(Convert.toDouble(tangShiUnpaid) + Convert.toDouble(takeOutUnpaid))));

        EmailPayType email_pay_type_unpaid = new EmailPayType();
        email_pay_type_unpaid.setPayType(unpaid_title);
        email_pay_type_unpaid.setTangshi(tangShiUnpaid);
        email_pay_type_unpaid.setTakeout(takeOutUnpaid);
        email_pay_type_unpaid.setTotal(totalUnpaid);

        String refunded_title = zh ? "已退款" : "Refunded";
        String tangShiReturnCash = BigDecimalUtil.format(BigDecimal.valueOf(showMoneyDouble(reportService.returnCashForTangShi(shopId))));
        String takeOutReturnCash = BigDecimalUtil.format(BigDecimal.valueOf(showMoneyDouble(reportService.returnCashForTakeOut(shopId))));
        String returnCash = BigDecimalUtil.format(BigDecimal.valueOf(showMoneyDouble(Convert.toDouble(tangShiReturnCash) + Convert.toDouble(takeOutReturnCash))));

        EmailPayType email_pay_type_refunded = new EmailPayType();
        email_pay_type_refunded.setPayType(refunded_title);
        email_pay_type_refunded.setTangshi(tangShiReturnCash);
        email_pay_type_refunded.setTakeout(takeOutReturnCash);
        email_pay_type_refunded.setTotal(returnCash);

        //统计人数以及单数
        String guest_title = zh ? "客流量" : "Guest";
        String dinners = BigDecimalUtil.format(BigDecimal.valueOf(getInt(reportService.dinners(shopId))));
        EmailPayType guest_email_pay_type = new EmailPayType();
        guest_email_pay_type.setPayType(guest_title);
        guest_email_pay_type.setTotal(dinners + (zh ? "人" : " People"));

        String order_title = zh ? "訂單量" : "Transactions";
        String orderNumber = BigDecimalUtil.format(BigDecimal.valueOf(getInt(reportService.orderNumber(shopId))));
        EmailPayType order_email_pay_type = new EmailPayType();
        order_email_pay_type.setPayType(order_title);
        order_email_pay_type.setTotal(orderNumber + (zh ? "單" : " Checks"));

        String delete_order_title = zh ? "刪除訂單" : "Deleted Check";
        int deleteOrderNumber = reportService.deleteOrderNumber(shopId);
        EmailPayType delete_order_email_pay_type = new EmailPayType();
        delete_order_email_pay_type.setPayType(delete_order_title);
        delete_order_email_pay_type.setTotal(String.valueOf(deleteOrderNumber) + (zh ? "單" : " Checks"));

        String delete_item_title = zh ? "刪除菜品" : "Deleted Dish";
        int deleteOrderItemNumber = reportService.deleteOrderItemNumber(shopId);
        EmailPayType delete_order_item_email_pay_type = new EmailPayType();
        delete_order_item_email_pay_type.setPayType(delete_item_title);
        delete_order_item_email_pay_type.setTotal(String.valueOf(deleteOrderItemNumber) + (zh ? "個" : " Dishes"));

        String order_tangshi_title = zh ? "餐廳堂食" : "Dine Here";
        String tangShiOrderNumber = BigDecimalUtil.format(BigDecimal.valueOf(getInt(reportService.tangShiOrderNumber(shopId))));
        EmailPayType order_tang_shi_email_pay_type = new EmailPayType();
        order_tang_shi_email_pay_type.setPayType(order_tangshi_title);
        order_tang_shi_email_pay_type.setTotal(tangShiOrderNumber + (zh ? "單" : " Checks"));

        String order_take_out_title = zh ? "餐廳外賣" : "Take Out";
        String takeOutOrderNumber = BigDecimalUtil.format(BigDecimal.valueOf(getInt(reportService.takeOutOrderNumber(shopId))));
        EmailPayType order_take_out_email_pay_type = new EmailPayType();
        order_take_out_email_pay_type.setPayType(order_take_out_title);
        order_take_out_email_pay_type.setTotal(takeOutOrderNumber + (zh ? "單" : " Checks"));

        String take_out_plat_title = zh ? "外賣平台" : "Delivery Platform";
        String platOrderNumber = BigDecimalUtil.format(BigDecimal.valueOf(reportService.countPlatOrderNumber(shopId)));
        EmailPayType take_out_platform_email_pay_type = new EmailPayType();
        take_out_platform_email_pay_type.setPayType(take_out_plat_title);
        take_out_platform_email_pay_type.setTotal(platOrderNumber + (zh ? "單" : " Checks"));

        //数据准备到位，开始拼接
        StringBuilder sb = new StringBuilder();
        sb.append("<!DOCTYPE html>\n" +
                "<html lang=\"en\">\n" +
                "<head>\n" +
                "  <meta charset=\"UTF-8\"/>\n" +
                "  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"/>\n" +
                "  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/>\n" +
                "  <style>\n" +
//                "    *{margin: 0;padding: 0;box-sizing: border-box;font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Liberation Sans\", \"PingFang SC\", \"Microsoft YaHei\", \"Hiragino Sans GB\", \"Wenquanyi Micro Hei\", \"WenQuanYi Zen Hei\", \"ST Heiti\", SimHei, SimSun, \"WenQuanYi Zen Hei Sharp\", sans-serif;}\n" +
                "    *{margin: 0;padding: 0;box-sizing: border-box;}\n" +
                "    html, body{width: 100%;height: 100%;font-size: 20px;}\n" +
                "    .wrapper{width: 30rem;height: auto;display: flex;flex-direction: column;margin: 0 auto;padding: 1rem;}\n" +
                "  </style>\n" +
                "  <title>闭店总报告</title>\n" +
                "</head>\n" +
                "<body style='font-family: SimSun;'>");
        sb.append("  <div class=\"wrapper\">");

        sb.append(String.format("<section style=\"width: 100%%;\">\n" +
                "      <div style=\"text-align: center;font-size: 1.5rem;font-weight: bold;\">%s</div>\n" +
                "      <div style=\"text-align: center;margin: 1rem 0;\">（%s）</div>\n" +
                "    </section>", companyName, shopName));

        sb.append(String.format(" <section style=\"width: 100%%;\">\n" +
                "      <div style=\"display: flex;\">\n" +
                "        <span style=\"flex-shrink: 0;\">%s</span>\n" +
                "        <span style=\"flex: 1;\">%s</span>\n" +
                "      </div>\n", address_title, address));

        //如果是美国版本，添加剩下的信息
        if (config.isUsVersion()) {
            List<String> addressList = Arrays.asList(shopInDb.getUsCity(), ShopService.getInstance().getStateAbbreviation(shopInDb.getUsState()), shopInDb.getUsZip());
            String detailAddress = StringUtils.join(addressList, " ");
            int padding = 10;

            if (!zh) {
                padding = 14;
            }

            sb.append(String.format(" <div style=\"display: flex;\">\n" +
                    "        <span style=\"flex: 1; padding-left : %s%% \">%s</span>\n" +
                    "      </div>\n", padding, detailAddress));
        }

        if (isNotEmpty(phone)) {
            sb.append(String.format("      <div style=\"display: flex;\">\n" +
                    "        <span style=\"flex-shrink: 0;\">%s</span>\n" +
                    "        <span style=\"flex: 1;\">%s</span>\n" +
                    "      </div>\n", phone_title, phone));
        }

        sb.append(String.format("<div style=\"display: flex;padding-bottom: 0.5rem;\">\n" +
                "        <span style=\"flex-shrink: 0;\">%s</span>\n" +
                "        <span style=\"flex: 1;\">%s</span>\n" +
                "      </div>\n" +
                "    </section>", date_title, date));

        sb.append("  <section style=\"width: 100%;\">");
        sb.append(" <table border=\"1\" style=\"width: 100%;margin-bottom: 1rem;table-layout: fixed;border: none;\">");

        sb.append(String.format("<tr style='font-weight:bold'>\n" +
                "            <td style=\"border: none;\">%s</td>\n" +
                "            <td style=\"border: none;text-align: center;\">%s</td>\n" +
                "            <td style=\"border: none;text-align: center;\">%s</td>\n" +
                "            <td style=\"border: none;text-align: center;\">%s</td>\n" +
                "          </tr>", payType_title, tangshi_title, takeout_title, total_title));

        //依次添加具体的销售额
        sb.append(" <tbody>");
        for (EmailPayType emailPayType : emailPayTypeList.subList(0, emailPayTypeList.size() - 1)) {
            sb.append(String.format("<tr>\n" +
                    "            <td style=\"border: none;\">%s</td>\n" +
                    "            <td style=\"border: none;text-align: center;\">%s</td>\n" +
                    "            <td style=\"border: none;text-align: center;\">%s</td>\n" +
                    "            <td style=\"border: none;text-align: center;\">%s</td>\n" +
                    "          </tr>", emailPayType.getPayType(), BigDecimalUtil.format(BigDecimal.valueOf(Convert.toDouble(emailPayType.getTangshi()))), BigDecimalUtil.format(BigDecimal.valueOf(Convert.toDouble(emailPayType.getTakeout()))), BigDecimalUtil.format(BigDecimal.valueOf(Convert.toDouble(emailPayType.getTotal())))));
        }

        //外卖平台单独处理格式
        EmailPayType take_out_plat_pay_type = emailPayTypeList.get(emailPayTypeList.size() - 1);
        sb.append(String.format(" <tr>\n" +
                "            <td style=\"border: none;padding-bottom: 0.5rem;\">%s</td>\n" +
                "            <td style=\"border: none;text-align: center;padding-bottom: 0.5rem;\">%s</td>\n" +
                "            <td style=\"border: none;text-align: center;padding-bottom: 0.5rem;\">%s</td>\n" +
                "            <td style=\"border: none;text-align: center;padding-bottom: 0.5rem;\">%s</td>\n" +
                "          </tr>", take_out_plat_pay_type.getPayType(), BigDecimalUtil.format(BigDecimal.valueOf(Convert.toDouble(take_out_plat_pay_type.getTangshi()))), BigDecimalUtil.format(BigDecimal.valueOf(Convert.toDouble(take_out_plat_pay_type.getTakeout()))), BigDecimalUtil.format(BigDecimal.valueOf(Convert.toDouble(take_out_plat_pay_type.getTotal())))));

        //添加营业额
        sb.append(String.format(" <tr>\n" +
                "            <td style=\"border: none;border-top: 1px dashed #000;padding-top: 0.5rem;font-weight: bold;font-size: 1.5rem;padding-bottom: 0.5rem;\">%s</td>\n" +
                "            <td style=\"border: none;text-align: center;border-top: 1px dashed #000;padding-top: 0.5rem;font-weight: bold;padding-bottom: 0.5rem;\">%s</td>\n" +
                "            <td style=\"border: none;text-align: center;border-top: 1px dashed #000;padding-top: 0.5rem;font-weight: bold;padding-bottom: 0.5rem;\">%s</td>\n" +
                "            <td style=\"border: none;text-align: center;border-top: 1px dashed #000;padding-top: 0.5rem;font-weight: bold;padding-bottom: 0.5rem;\">%s</td>\n" +
                "          </tr>", sale_total_title, tangshi_total, take_out_total, total_double));

        //添加服务费
        sb.append(String.format(" <tr>\n" +
                "            <td style=\"border: none;border-top: 1px dashed #000;padding-top: 0.5rem;\">%s</td>\n" +
                "            <td style=\"border: none;border-top: 1px dashed #000;padding-top: 0.5rem;\"></td>\n" +
                "            <td style=\"border: none;border-top: 1px dashed #000;padding-top: 0.5rem;\"></td>\n" +
                "            <td style=\"border: none;text-align: center;border-top: 1px dashed #000;padding-top: 0.5rem;\">%s</td>\n" +
                "          </tr>", service_fee_pay_type.getPayType(), service_fee_pay_type.getTotal()));

        //添加小費
        if (canPrintTips(shopInDb)) {
            sb.append(String.format(" <tr>\n" +
                    "            <td style=\"border: none;\">%s</td>\n" +
                    "            <td style=\"border: none;\"></td>\n" +
                    "            <td style=\"border: none;\"></td>\n" +
                    "            <td style=\"border: none;text-align: center;\">%s</td>\n" +
                    "          </tr>", tips_pay_type.getPayType(), tips_pay_type.getTotal()));
        }

        //添加消費稅
        if (Config.getInstance().isUsVersion()) {
            sb.append(String.format(" <tr>\n" +
                    "            <td style=\"border: none;\">%s</td>\n" +
                    "            <td style=\"border: none;\"></td>\n" +
                    "            <td style=\"border: none;\"></td>\n" +
                    "            <td style=\"border: none;text-align: center;\">%s</td>\n" +
                    "          </tr>", String.format("%s(%s%%)", sale_tax_pay_type.getPayType(), shopInDb.getShopConfig().getConsumptionTax()), sale_tax_pay_type.getTotal()));
        }

        //餐盒费，外卖折扣
        for (EmailPayType emailPayType : Arrays.asList(package_fee_pay_type, take_out_fee_pay_type)) {
            sb.append(String.format(" <tr>\n" +
                    "            <td style=\"border: none;\">%s</td>\n" +
                    "            <td style=\"border: none;\"></td>\n" +
                    "            <td style=\"border: none;\"></td>\n" +
                    "            <td style=\"border: none;text-align: center;\">%s</td>\n" +
                    "          </tr>", emailPayType.getPayType(), emailPayType.getTotal()));
        }

        //添加优惠卷
        sb.append(String.format(" <tr>\n" +
                "            <td style=\"border: none;padding-bottom: 0.5rem;\">%s</td>\n" +
                "            <td style=\"border: none;padding-bottom: 0.5rem;\"></td>\n" +
                "            <td style=\"border: none;padding-bottom: 0.5rem;\"></td>\n" +
                "            <td style=\"border: none;text-align: center;padding-bottom: 0.5rem;\">%s</td>\n" +
                "          </tr>", ticket_fee_pay_type.getPayType(), ticket_fee_pay_type.getTotal()));

        //添加未支付
        sb.append(String.format("<tr>\n" +
                "            <td style=\"border: none;border-top: 1px dashed #000;padding-top: 0.5rem;\">%s</td>\n" +
                "            <td style=\"border: none;text-align: center;border-top: 1px dashed #000;padding-top: 0.5rem;\">%s</td>\n" +
                "            <td style=\"border: none;text-align: center;border-top: 1px dashed #000;padding-top: 0.5rem;\">%s</td>\n" +
                "            <td style=\"border: none;text-align: center;border-top: 1px dashed #000;padding-top: 0.5rem;\">%s</td>\n" +
                "          </tr>", email_pay_type_unpaid.getPayType(), email_pay_type_unpaid.getTangshi(), email_pay_type_unpaid.getTakeout(), email_pay_type_unpaid.getTotal()));

        //已退款
        sb.append(String.format(" <tr>\n" +
                "            <td style=\"border: none;\">%s</td>\n" +
                "            <td style=\"border: none;text-align: center;\">%s</td>\n" +
                "            <td style=\"border: none;text-align: center;\">%s</td>\n" +
                "            <td style=\"border: none;text-align: center;\">%s</td>\n" +
                "          </tr>", email_pay_type_refunded.getPayType(), email_pay_type_refunded.getTangshi(), email_pay_type_refunded.getTakeout(), email_pay_type_refunded.getTotal()));

        sb.append(" </tbody>");
        sb.append("</table>\n" +
                "    </section>");

        sb.append(" <section>");
        //添加客流量，订单量，删除订单，删除菜品，餐厅堂食，餐厅外卖，外卖平台
        for (EmailPayType emailPayType : Arrays.asList(guest_email_pay_type, order_email_pay_type,
                delete_order_email_pay_type, delete_order_item_email_pay_type,
                order_tang_shi_email_pay_type, order_take_out_email_pay_type, take_out_platform_email_pay_type)) {
            sb.append(String.format("<div style=\"display: flex;justify-content: space-between;\">\n" +
                    "        <span>%s</span>\n" +
                    "        <span style=\"flex: 1;border-top: 1px dashed #000;height: 0;position: relative;top: 0;bottom: 0;margin: auto 1rem;\"></span>\n" +
                    "        <span style='float:right'>%s</span>\n" +
                    "      </div>", emailPayType.getPayType(), emailPayType.getTotal()));
        }
        sb.append(" </section>");

        /*********************************************************************************************************************************
         *  会员系统
         * *************************************************************************************************************************/
        if (getBoolean(company.getCompanyConfig().getCustomerOpen())) {
            //添加会员报告
            sb.append(String.format("<section style=\"width: 100%%;\">\n" +
                    "      <div style=\"text-align: center;font-size: 1.5rem;font-weight: bold;border-top: 1px dashed #000;padding-top: 0.5rem;margin: 1rem 0;padding-top: 1rem;\">%s</div>\n" +
                    "    </section>", config.close_customer_report_title(zh)));

            sb.append(String.format("<section style=\"width: 100%%;\">\n" +
                            "      <table border=\"1\" style=\"width: 100%%;table-layout: fixed;border: none;\">\n" +
                            "<tr>" +
                            "<td style=\"border: none;\">%s</td>\n" +
                            "            <td style=\"border: none;text-align: center;\">%s</td>\n" +
                            "            <td style=\"border: none;text-align: center;\"></td>\n" +
                            "            <td style=\"border: none;text-align: center;\">%s</td>\n" +
                            "          </tr>"
                    , config.close_report_pay_type_title(zh), config.customer_report_vip_title(zh), config.customer_report_vip_sale_title(zh)));

            sb.append("        <tbody>");

            //添加中间的支付方式
            List<PrinterService.PrintPayType> payTypeList = generateCustomerReport(shopId, zh);
            for (PrinterService.PrintPayType printPayType : payTypeList) {
                sb.append(String.format("   <tr>" +
                        "<td style=\"border: none;\">%s</td>\n" +
                        "            <td style=\"border: none;text-align: center;\">%s</td>\n" +
                        "            <td style=\"border: none;text-align: center;\"></td>\n" +
                        "            <td style=\"border: none;text-align: center;\">%s</td>\n" +
                        "          </tr>", printPayType.payTypeName, BigDecimalUtil.format(BigDecimal.valueOf(printPayType.vipFee)), BigDecimalUtil.format(BigDecimal.valueOf(printPayType.vipFeeAndOther))));
            }

            //添加总计
            sb.append(String.format("<tr>\n" +
                            "            <td style=\"border: none;border-top: 1px dashed #000;padding-top: 0.5rem;font-weight: bold;font-size: 1.5rem;padding-bottom: 0.5rem;border-bottom: 1px dashed\">%s</td>\n" +
                            "            <td style=\"border: none;text-align: center;border-top: 1px dashed #000;padding-top: 0.5rem;font-weight: bold;padding-bottom: 0.5rem;border-bottom: 1px dashed\">%s</td>\n" +
                            "            <td style=\"border: none;text-align: center;border-top: 1px dashed #000;padding-top: 0.5rem;font-weight: bold;padding-bottom: 0.5rem;border-bottom: 1px dashed\"></td>\n" +
                            "            <td style=\"border: none;text-align: center;border-top: 1px dashed #000;padding-top: 0.5rem;font-weight: bold;padding-bottom: 0.5rem;border-bottom: 1px dashed\">%s</td>\n" +
                            "          </tr>", config.customer_report_vip_total_title(zh),
                    BigDecimalUtil.format(BigDecimal.valueOf(showMoneyDouble(payTypeList.stream().mapToDouble(p -> getDouble(p.vipFee)).sum()))),
                    BigDecimalUtil.format(BigDecimal.valueOf(showMoneyDouble(payTypeList.stream().mapToDouble(p -> getDouble(p.vipFeeAndOther)).sum())))
            ));

            //美国版本，添加净营收
            if (config.isUsVersion()) {
                CloseReportService.getInstance().addNetSales(sb, shopInDb, zh);
                CloseReportService.getInstance().addSaleWithTax(sb, closeReport, zh);
            }

            sb.append("</tbody>\n" +
                    "      </table>\n" +
                    "    </section>");
        } else {
            //美国版本，添加净营收
            if (config.isUsVersion()) {
                sb.append("<section style=\"width: 100%;\">\n" + "      <table border=\"1\" style=\"width: 100%;table-layout: fixed;border: none;border-bottom: 1px dashed;\">\n");
                sb.append("        <tbody>");
                CloseReportService.getInstance().addNetSales(sb, shopInDb, zh);

                CloseReportService.getInstance().addSaleWithTax(sb, closeReport, zh);

                sb.append("</tbody>\n" +
                        "      </table>\n" +
                        "    </section>");
            }
        }


        sb.append("</div>\n" +
                "</body>\n" +
                "</html>" +
                "");

        String finalString = sb.toString();

        //这里只是为了方便调试
        File tmpFile = MongoId.getInstance().generateTmpFile("html");
        FileUtil.writeToFile(tmpFile, finalString);
        logger.info("the file {}", tmpFile.getAbsolutePath());

        return finalString;
    }

    /**
     * 产生所有的报告
     *
     * @param shopId
     * @return
     * @throws Exception
     */
    public List<PrinterService.PrintPayType> generateCustomerReport(Long shopId, boolean zh) throws Exception {
        PayTypeService payTypeService = PayTypeService.getInstance();
        List<PayType> payTypeList = payTypeService.findPayTypeListCache_Stripe(shopId);

        ReportService reportService = ReportService.getInstance();
        Shop shop = ShopService.getInstance().findShopById(shopId);
        List<PrinterService.PrintPayType> printerPayTypeList = new ArrayList<>();

        for (PayType payType : payTypeList) {
            int payTypeInt = payType.getType();
            PrinterService.PrintPayType printPayType = new PrinterService.PrintPayType();
            printPayType.payTypeName = payTypeService.getPayTypeName(payType, zh);
            printPayType.payType = payTypeInt;
            printPayType.vipFee = reportService.sumVip(shop, payTypeInt);
            double otherFee = reportService.sumCash(shopId, payTypeInt);
            printPayType.vipFeeAndOther = printPayType.vipFee + otherFee;
            printerPayTypeList.add(printPayType);
        }

        return printerPayTypeList;
    }

    /**
     * 字符串转数字
     *
     * @param s
     * @return
     */
    public double stringToDouble(String s) {
        try {
            return Double.valueOf(s);
        } catch (Exception e) {
            return 0d;
        }
    }


    /**
     * 添加每一种支付方式
     *
     * @param zh
     * @return
     */
    private List<EmailPayType> addPayTypeCash(boolean zh, Long shopId) throws Exception {
        PayTypeService payTypeService = PayTypeService.getInstance();
        List<PayType> payTypeList = payTypeService.findPayTypeListCache_Stripe(shopId);

        List<EmailPayType> emailPayTypeList = new ArrayList<>();

        for (PayType payType : payTypeList) {
            EmailPayType emailPayType = new EmailPayType();
            emailPayType.setPayType(payTypeService.getPayTypeName(payType, zh));

            //添加堂食金额
            String tangShiCash = String.valueOf(ReportService.getInstance().sumTangShiCash(shopId, payType.getType()));
            emailPayType.setTangshi(tangShiCash);

            //添加外卖金额
            String takeOutCash = String.valueOf(ReportService.getInstance().sumTakeOutCash(shopId, payType.getType()));
            emailPayType.setTakeout(takeOutCash);

            //总额
            String total = String.valueOf(ReportService.getInstance().sumCash(shopId, payType.getType()));
            emailPayType.setTotal(total);

            emailPayTypeList.add(emailPayType);
        }

        return emailPayTypeList;
    }

    /**
     * 依次生成时间序列号
     *
     * @param orderList
     */
    public void resetTimeNo(List<Order> orderList, int timeFix) throws Exception {
        int timeNoIndex = 1;
        for (Order order : orderList) {
            order.setBusinessEndShow(false);
            order.setTimeNo(timeNoIndex);
            order.setTimeFix(timeFix);
            commonService.save(order);
            timeNoIndex = timeNoIndex + 1;
        }
    }

    /**
     * 查询分店的所有打印机
     *
     * @param shopId
     * @return
     */
    public List<Printer> findPrinterList(Long shopId) throws Exception {
        return findEntityListByShopId(shopId, Printer.class);
    }

    /**
     * 判断分店是否有未支付完成的订单
     *
     * @return
     */
    public boolean checkShopUnpaidOrder(Long shopId) throws Exception {
        //判断是否有部分付款的订单
        Shop shopInDb = ShopService.getInstance().findShopById(shopId);
        List<Order> orderList = OrderService.getInstance().queryOpenTimeOrderList(shopInDb);

        Optional<Order> partPayedOrder = orderList.stream().filter(order -> {
            int status = getInt(order.getStatus());
            logger.info("status is {}", status);
            boolean end = status == Constant.ORDER_STATUS_END;

            //已经结束的订单，不算未支付
            if (end) {
                return false;
            }

            //初始状态，但是已退款的订单，也算是结束的订单，不算未支付的订单
            if (status == Constant.ORDER_STATUS_STARTING && getBoolean(order.getReturnCash())) {
                return false;
            }

            return true;
        }).findAny();

        logger.info("partPayedOrder exist {}", partPayedOrder.isPresent());

        return partPayedOrder.isPresent();
    }

    /**
     * 获取分店ID
     *
     * @param shop
     * @return
     */
    public Long getShopId(Shop shop) {
        if (shop.getId() != null) {
            return shop.getId();
        }

        return shop.getShopId();
    }

    private void orderReport(Long shopId, Long parentOrderId) throws Exception {
        if (parentReport(shopId, parentOrderId)) return;

        subOrderReport(shopId, parentOrderId);
    }

    /**
     * 部分支付完成的订单的统计
     * 比如分钱结账，只统计在支付方式 订单类型 和 服务员那里了
     * 比如分菜结账，可以所有的都统计
     *
     * @param shopId
     * @param parentOrderId
     */
    private void subOrderReport(Long shopId, Long parentOrderId) throws Exception {
        List<Order> subOrderList = OrderService.getInstance().findOrderByParentId(parentOrderId, shopId).stream().filter(o -> {
            //要已付款，并且没有退款的订单
            return !getBoolean(o.getExpiredOrder()) && getBoolean(o.getPayed()) && !getBoolean(o.getReturnCash());
        }).collect(Collectors.toList());

        logger.info("will add report");
        //分钱的 只能统计在支付方式 订单类型 和 服务员那里了
        subOrderList.forEach(o -> {
            addSubOrderToReport(o);
        });
    }

    /**
     * 将子订单的数据添加到报表当中
     *
     * @param order
     */
    private void addSubOrderToReport(Order order) {
        try {
            if (getInt(order.getUserSelectPayType()) == USER_SELECT_PAY_TYPE_PART_FOOD) {
                dealOrderCatalogMapping(order);
                dealOrderItemMapping(order);
            }

            //将订单种也添加到报表中
            dealOrderTypeMapping(order);
            dealOrderPayTypeMapping(order);
            //按照百分比来计算
            dealOrderDiscountTicketMapping(order);
            dealOrderWaiterMapping(order);

            //添加优惠卷折扣
            dealTakeOutDiscountFeeMapping(order);

            //添加消费税
            dealConsumptionTaxMapping(order);

            //添加小费
            dealTipsMapping(order);
        } catch (Exception e) {
            logger.error("e", e);
        }
    }

    /**
     * 支付完成的订单的统计
     *
     * @param shopId
     * @param parentOrderId
     * @return
     * @throws Exception
     */
    private boolean parentReport(Long shopId, Long parentOrderId) throws Exception {
        //如果是父订单支付成功，那么直接统计父订单好了
        Order parentOrder = OrderService.getInstance().findOrder(shopId, parentOrderId);
        if (getBoolean(parentOrder.getParentOrder())
                && getBoolean(parentOrder.getPayed())
                && getInt(parentOrder.getStatus()) == ORDER_STATUS_END) {
            dealOrderCatalogMapping(parentOrder);
            dealOrderItemMapping(parentOrder);
            dealOrderTypeMapping(parentOrder);


            dealOrderWaiterMapping(parentOrder);

            //单独处理支付方式的映射
            List<Order> subOrderList = OrderService.getInstance().findOrderByParentId(parentOrderId, shopId).stream().filter(o -> {
                //只统计支付订单，要已付款，并且没有退款的订单
                return getBoolean(o.getPayed()) && !getBoolean(o.getReturnCash()) && !getBoolean(o.getSubOrder());
            }).collect(Collectors.toList());

            for (Order order : subOrderList) {
                dealOrderPayTypeMapping(order);
                dealOrderDiscountTicketMapping(order);

                //处理小费
                dealTipsMapping(order);

                //处理消费税
                dealConsumptionTaxMapping(order);
            }

            //添加服务费
            dealOrderServiceMapping(parentOrder);
            //添加餐盒费
            dealOrderPackingfeeMapping(parentOrder);
            //添加外卖折扣费用
            dealTakeOutDiscountFeeMapping(parentOrder);
            //添加优惠卷折扣
            dealDiscountTicketFeeMapping(parentOrder);
            //处理特别菜
            dealSpecialOrderMapping(parentOrder);
            return true;
        }

        return false;
    }

    /**
     * 添加每个订单的服务费
     *
     * @param order
     */
    public void dealOrderServiceMapping(Order order) {
        //如果是0，則不用生成服務費
        if (getDouble(order.getServiceFee()) <= 0) {
            return;
        }

        OrderCatalogMapping orderCatalogMapping = new OrderCatalogMapping();
        orderCatalogMapping.setOrderId(order.getId());
        orderCatalogMapping.setShopId(order.getShopId());
        orderCatalogMapping.setCatalogId(ORDER_CATALOG_SERVICE_FEE_ID);

        try {
            orderCatalogMapping.setCatalogName_zh("服務費");
            orderCatalogMapping.setCatalogName_en("Service Charge");
            orderCatalogMapping.setCash(order.getServiceFee());
            orderCatalogMapping.setCreateTime(order.getCreateTime());
            orderCatalogMapping.setCatalogCount(1);
            Long reportDataTime = getReportDataTime(order);
            order.setCreateTime(reportDataTime);
            commonService.createObject(orderCatalogMapping);
        } catch (Exception e) {
            logger.error("e", e);
        }
    }

    /**
     * 处理餐盒费
     *
     * @param order
     */
    public void dealOrderPackingfeeMapping(Order order) {
        //如果是0，則不用生成服務費
        if (getDouble(order.getPackageFee()) <= 0) {
            return;
        }

        OrderCatalogMapping orderCatalogMapping = new OrderCatalogMapping();
        orderCatalogMapping.setOrderId(order.getId());
        orderCatalogMapping.setShopId(order.getShopId());
        orderCatalogMapping.setCatalogId(ORDER_CATALOG_PACKING_FEE_ID);

        try {
            orderCatalogMapping.setCatalogName_zh("餐盒費");
            orderCatalogMapping.setCatalogName_en("Packing Fee");
            orderCatalogMapping.setCash(order.getPackageFee());
            orderCatalogMapping.setCreateTime(order.getCreateTime());
            orderCatalogMapping.setCatalogCount(1);
            Long reportDataTime = getReportDataTime(order);
            orderCatalogMapping.setCreateTime(reportDataTime);

            commonService.createObject(orderCatalogMapping);
        } catch (Exception e) {
            logger.error("e", e);
        }
    }

    /**
     * 处理外卖打折费
     *
     * @param order
     */
    public void dealTakeOutDiscountFeeMapping(Order order) {
        //如果是0，則不用生成服務費
        if (getDouble(order.getTakeOutDiscountFee()) <= 0) {
            return;
        }

        OrderCatalogMapping orderCatalogMapping = new OrderCatalogMapping();
        orderCatalogMapping.setOrderId(order.getId());
        orderCatalogMapping.setShopId(order.getShopId());
        orderCatalogMapping.setCatalogId(ORDER_CATALOG_TAKE_OUT_DISCOUNT_FEE_ID);

        try {
            orderCatalogMapping.setCatalogName_zh("外賣折扣");
            orderCatalogMapping.setCatalogName_en("Take out discount");
            orderCatalogMapping.setCash(-order.getTakeOutDiscountFee());
            orderCatalogMapping.setCreateTime(order.getCreateTime());
            orderCatalogMapping.setCatalogCount(1);
            Long reportDataTime = getReportDataTime(order);
            orderCatalogMapping.setCreateTime(reportDataTime);
            commonService.createObject(orderCatalogMapping);
        } catch (Exception e) {
            logger.error("e", e);
        }
    }

    /**
     * 处理优惠卷的折扣
     *
     * @param order
     */
    public void dealDiscountTicketFeeMapping(Order order) {
        double ticketDiscountFee = showMoneyDouble(order.getDiscountFee());
        if (ticketDiscountFee <= 0) {
            return;
        }

        OrderCatalogMapping orderCatalogMapping = new OrderCatalogMapping();
        orderCatalogMapping.setOrderId(order.getId());
        orderCatalogMapping.setShopId(order.getShopId());
        orderCatalogMapping.setCatalogId(ORDER_CATALOG_DISCOUNT_TICKET_FEE_ID);

        try {
            orderCatalogMapping.setCatalogName_zh("優惠券");
            orderCatalogMapping.setCatalogName_en("Coupon");
            orderCatalogMapping.setCash(-ticketDiscountFee);
            orderCatalogMapping.setCreateTime(order.getCreateTime());
            Long reportDataTime = getReportDataTime(order);
            orderCatalogMapping.setCreateTime(reportDataTime);
            orderCatalogMapping.setCatalogCount(1);
            commonService.createObject(orderCatalogMapping);
        } catch (Exception e) {
            logger.error("e", e);
        }
    }

    /**
     * 处理特别菜
     *
     * @param order
     */
    public void dealSpecialOrderMapping(Order order) {
        List<OrderItem> orderItemList = filterSpecialOrderItem(order);
        if (isEmpty(orderItemList)) {
            return;
        }

        double sum = orderItemList.stream().mapToDouble(o -> o.getItemPrice() * o.getCount()).sum();

        OrderCatalogMapping orderCatalogMapping = new OrderCatalogMapping();
        orderCatalogMapping.setOrderId(order.getId());
        orderCatalogMapping.setShopId(order.getShopId());
        orderCatalogMapping.setCatalogId(ORDER_CATALOG_SPECIAL_ITEM_ID);

        try {
            orderCatalogMapping.setCatalogName_zh(SPECIAL_ITEM_NAME_ZH);
            orderCatalogMapping.setCatalogName_en(SPECIAL_ITEM_NAME_EN);
            orderCatalogMapping.setCash(showMoneyDouble(sum));
            orderCatalogMapping.setCreateTime(order.getCreateTime());
            Long reportDataTime = getReportDataTime(order);
            orderCatalogMapping.setCreateTime(reportDataTime);
            orderCatalogMapping.setCatalogCount(orderItemList.stream().mapToInt(p -> p.getCount()).sum());
            commonService.createObject(orderCatalogMapping);
        } catch (Exception e) {
            logger.error("e", e);
        }
    }

    /**
     * 过滤特别菜
     *
     * @param order
     * @return
     */
    public List<OrderItem> filterSpecialOrderItem(Order order) {
        return getList(order.getOrderItemDetailList()).stream().filter(o -> equals(o.getItemId(), SPECIAL_ITEM)).collect(Collectors.toList());
    }

    /**
     * 处理订单与品类之间的映射，只处理父订单生成的子订单
     *
     * @param order
     */
    public void dealOrderCatalogMapping(Order order) {
        HashMap<Long, CashAndCount> catalogMap = new HashMap<>();

        //先将每个菜的价格计算出来
        getList(order.getOrderItemDetailList()).forEach(orderItem -> {
            CatalogItemMapping catalogItemMapping = new CatalogItemMapping();
            catalogItemMapping.setItemId(orderItem.getItemId());
            try {
                CatalogItemMapping catalogItemMappingSearchResult = MappingService.getInstance().findCatalogItemMapping(orderItem.getItemId());

                if (catalogItemMappingSearchResult != null) {
                    Long catalogId = catalogItemMappingSearchResult.getCatalogId();
                    //添加金额
                    CashAndCount cashAndCount = catalogMap.getOrDefault(catalogId, new CashAndCount());
                    cashAndCount.setCash(cashAndCount.getCash() + orderItem.getItemPrice() * orderItem.getCount());

                    //添加计数
                    cashAndCount.setCount(cashAndCount.getCount() + orderItem.getCount());

                    catalogMap.put(catalogId, cashAndCount);
                }

                //如果还是套餐，那么将套餐里面的每个菜的单独标的价格添加进去
                if (getBoolean(orderItem.getSetMenu())) {
                    List<SetMenuOneItem> setMenuOneItemList = orderItem.getSetMenuOneItemList();
                    for (SetMenuOneItem setMenuOneItem : setMenuOneItemList) {
                        //统计加价的价格
                        List<Item> itemList = setMenuOneItem.getItemList();
                        for (Item item : itemList) {
                            CatalogItemMapping catalogItemMappingSearchResultInSetMenu = MappingService.getInstance().findCatalogItemMapping(orderItem.getItemId());

                            if (catalogItemMappingSearchResultInSetMenu != null) {
                                Long catalogId = catalogItemMappingSearchResultInSetMenu.getCatalogId();

                                //添加额外的金额
                                CashAndCount cashAndCount = catalogMap.getOrDefault(catalogId, new CashAndCount());
                                cashAndCount.setCash(cashAndCount.getCash() + getDouble(item.getSetMenuAddPrice()) * orderItem.getCount());

                                Integer countInOrder = item.getCountInOrder();
                                if (getInt(countInOrder) <= 0) {
                                    countInOrder = 1;
                                }

                                //添加计数
                                cashAndCount.setCount(cashAndCount.getCount() + orderItem.getCount() * countInOrder);

                                catalogMap.put(catalogId, cashAndCount);
                            }
                        }
                    }
                }

            } catch (Exception e) {
                logger.error("e", e);
            }
        });

        //依次生成
        catalogMap.entrySet().stream().forEach(entry -> {
            Long catalogId = entry.getKey();
            CashAndCount cashAndCount = entry.getValue();
            OrderCatalogMapping orderCatalogMapping = new OrderCatalogMapping();
            orderCatalogMapping.setOrderId(order.getId());
            orderCatalogMapping.setShopId(order.getShopId());
            orderCatalogMapping.setParentOrderId(getParentOrderId(order));
            orderCatalogMapping.setCatalogId(catalogId);

            try {
                Catalog catalog = commonService.findEntity(catalogId, Catalog.class);
                orderCatalogMapping.setCatalogName(catalog.getName());
                orderCatalogMapping.setCatalogName_zh(catalog.getName_zh());
                orderCatalogMapping.setCatalogName_en(catalog.getName_en());
                orderCatalogMapping.setCash(cashAndCount.getCash());
                orderCatalogMapping.setCatalogCount(cashAndCount.getCount());
                orderCatalogMapping.setCreateTime(order.getCreateTime());
                Long reportDataTime = getReportDataTime(order);
                orderCatalogMapping.setCreateTime(reportDataTime);
                commonService.createObject(orderCatalogMapping);

                //添加统计名称
                ShopSearchItemService.getInstance().addCatalog(orderCatalogMapping);
            } catch (Exception e) {
                logger.error("e", e);
            }
        });
    }

    /**
     * 处理订单与商品的映射关系
     *
     * @param order
     */
    public void dealOrderItemMapping(Order order) {
        Long orderId = order.getId();
        Long shopId = order.getShopId();

        //直接是数量乘以单价
        getList(order.getOrderItemDetailList()).forEach(p -> {
            CatalogItemMapping catalogItemMapping = new CatalogItemMapping();
            catalogItemMapping.setItemId(p.getItemId());
            try {
                {
                    //特别菜单独处理
                    if (equals(p.getItemId(), SPECIAL_ITEM)) {
                        Item item = new Item();
                        item.setId(SPECIAL_ITEM);
                        item.setName_zh(SPECIAL_ITEM_NAME_ZH);
                        item.setName_en(SPECIAL_ITEM_NAME_EN);
                        OrderItemMapping orderItemMapping = createOrderItemMapping(item, shopId, orderId);
                        orderItemMapping.setCreateTime(order.getCreateTime());
                        orderItemMapping.setParentOrderId(getParentOrderId(order));
                        orderItemMapping.setCash(showMoneyDouble(p.getItemPrice() * p.getCount()));
                        orderItemMapping.setItemCount(p.getCount());
                        commonService.createObject(orderItemMapping);
                    } else {
                        OrderItemMapping orderItemMapping = createOrderItemMapping(ItemService.getInstance().findItem(p.getItemId()), shopId, orderId);
                        orderItemMapping.setCreateTime(order.getCreateTime());
                        orderItemMapping.setCompanyId(order.getCompanyId());
                        orderItemMapping.setParentOrderId(getParentOrderId(order));
                        orderItemMapping.setCash(showMoneyDouble(p.getItemPrice() * p.getCount()));
                        orderItemMapping.setItemCount(p.getCount());
                        commonService.createObject(orderItemMapping);

                        //添加统计名称
                        ShopSearchItemService.getInstance().addItem(orderItemMapping);
                    }
                }

                //如果还是套餐，那么将套餐里面的每个菜的单独标的价格添加进去
                if (getBoolean(p.getSetMenu())) {
                    List<SetMenuOneItem> setMenuOneItemList = p.getSetMenuOneItemList();
                    for (SetMenuOneItem setMenuOneItem : setMenuOneItemList) {
                        //统计加价的价格
                        List<Item> itemList = setMenuOneItem.getItemList();
                        for (Item item : itemList) {
                            OrderItemMapping orderItemMapping = createOrderItemMapping(item, shopId, orderId);
                            orderItemMapping.setParentOrderId(getParentOrderId(order));
                            orderItemMapping.setCreateTime(order.getCreateTime());
                            orderItemMapping.setCash(item.getSetMenuAddPrice() * p.getCount());
                            Long reportDataTime = getReportDataTime(order);
                            orderItemMapping.setCreateTime(reportDataTime);
                            orderItemMapping.setItemCount(p.getCount());
                            orderItemMapping.setCompanyId(order.getCompanyId());
                            commonService.createObject(orderItemMapping);

                            //添加统计名称
                            ShopSearchItemService.getInstance().addItem(orderItemMapping);
                        }
                    }
                }
            } catch (Exception e) {
                logger.error("e", e);
            }
        });
    }

    /**
     * 创建映射关系
     *
     * @param item
     * @param shopId
     * @param orderId
     * @return
     */
    private OrderItemMapping createOrderItemMapping(Item item, Long shopId, Long orderId) {
        OrderItemMapping itemMapping = new OrderItemMapping();
        itemMapping.setItemId(item.getId());
        itemMapping.setShopId(shopId);
        itemMapping.setOrderId(orderId);
        itemMapping.setItemName(item.getName_en());
        itemMapping.setItemName_zh(item.getName_zh());
        itemMapping.setItemName_en(item.getName_en());

        return itemMapping;
    }

    /**
     * 处理相关优惠卷映射
     *
     * @param order
     */
    public void dealOrderDiscountTicketMapping(Order order) throws Exception {
        Long orderId = order.getId();
        Long shopId = order.getShopId();
        Shop shop = ShopService.getInstance().findShopById(shopId);
        Long companyId = shop.getCompanyId();

        //ticketCodeList 每一次都要计算,只需要按照当前订单来匹配就可以了
        List<String> ticketCodeList = order.getTicketCodeList();

        if (isNotEmpty(ticketCodeList)) {
            List<DiscountTicket> discountTicketList = ticketCodeList.stream().map(code -> {
                try {
                    return DiscountTicketService.getInstance().findTicketByCode(code, companyId);
                } catch (Exception e) {
                    logger.error("e", e);
                    return null;
                }
            }).filter(p -> p != null).collect(Collectors.toList());

            for (DiscountTicket ticket : discountTicketList) {
                OrderDiscountTicketMapping orderDiscountTicketMapping = new OrderDiscountTicketMapping();
                List<TicketDiscountFee> ticketDiscountFeeList = order.getTicketDiscountFeeList();
                TicketDiscountFee ticketDiscountFee = OrderService.getInstance().filterTicketDiscountFee(ticketDiscountFeeList, ticket.getCode());

                if (ticketDiscountFee != null) {
                    double saveCash = ticketDiscountFee.getFee();
                    orderDiscountTicketMapping.setShopId(shopId);
                    orderDiscountTicketMapping.setOrderId(orderId);
                    orderDiscountTicketMapping.setCash(saveCash);
                    orderDiscountTicketMapping.setName(ticket.getName());
                    orderDiscountTicketMapping.setCount(1);
                    Long reportDataTime = getReportDataTime(order);
                    orderDiscountTicketMapping.setCreateTime(reportDataTime);
                    commonService.createObject(orderDiscountTicketMapping);
                }
            }
        }
    }

    /**
     * 添加点餐种类报表
     *
     * @param order
     */
    public void dealOrderTypeMapping(Order order) throws Exception {
        Long orderId = order.getId();
        Long shopId = order.getShopId();

        if (order.getOrderType() == null) {
            logger.error("order type is empty");
            return;
        }

        try {
            logger.info("deal with order pay type {}", orderId);
            OrderTypeMapping orderTypeMapping = new OrderTypeMapping();
            orderTypeMapping.setShopId(shopId);
            orderTypeMapping.setOrderId(orderId);
            orderTypeMapping.setParentOrderId(getParentOrderId(order));
            //添加优惠券的金额
            orderTypeMapping.setCash(showMoneyDouble(order.getUserCost() + showMoneyDouble(order.getRealDiscountFee())));
            Long reportDataTime = getReportDataTime(order);
            orderTypeMapping.setCreateTime(reportDataTime);
            Integer orderType = order.getOrderType();

            if (orderType == ORDER_TYPE_TANG_SHI || orderType == ORDER_TYPE_QUICK_FOOD) {
                orderTypeMapping.setOrderType(ORDER_REPORT_TYPE_TANG_SHI);
            } else if (orderType == ORDER_TYPE_TAKE_OUT) {
                orderTypeMapping.setOrderType(ORDER_REPORT_TYPE_TAKE_OUT);
            } else if (orderType == ORDER_REPORT_TYPE_TAKE_OUT_PLAT) {
                orderTypeMapping.setOrderType(ORDER_REPORT_TYPE_TAKE_OUT_PLAT);
            }

            orderTypeMapping.setCreateTime(order.getCreateTime());
            commonService.createObject(orderTypeMapping);
        } catch (Exception e) {
            logger.error("e", e);
        }
    }

    /**
     * 处理消费税
     *
     * @param order
     */
    public void dealConsumptionTaxMapping(Order order) {
        logger.info("deal order {}", order.getId());
        //只有美国版本才处理消费税
        if (!Config.getInstance().isUsVersion()) {
            logger.info("not us version");
            return;
        }

        Double consumptionTax = order.getConsumptionTax();
        //没有消费税
        if (getDouble(consumptionTax) <= 0) {
            logger.info("has no consumption tax");
            return;
        }

        //失效订单不处理
        if (getBoolean(order.getExpiredOrder())) {
            logger.info("expired order");
            return;
        }

        //已退款的不处理
        if (getBoolean(order.getReturnCash())) {
            logger.info("return cash");
            return;
        }

        //添加映射
        OrderCatalogMapping orderCatalogMapping = new OrderCatalogMapping();
        orderCatalogMapping.setShopId(order.getShopId());
        orderCatalogMapping.setOrderId(order.getId());
        orderCatalogMapping.setCatalogId(ORDER_CATALOG_CONSUMPTION_ID);
        orderCatalogMapping.setCatalogName_zh("消費稅");
        orderCatalogMapping.setCatalogName_en("Sales Tax");
        orderCatalogMapping.setCash(order.getConsumptionTax());
        orderCatalogMapping.setCatalogCount(1);
        orderCatalogMapping.setCreateTime(order.getCreateTime());

        ThreadContext.put(THREAD_LOCAL_VARIABLE_NO_NEED_WRITE_OPERATE_LOG, "true");
        try {
            Long reportDataTime = getReportDataTime(order);
            order.setCreateTime(reportDataTime);
            commonService.createObject(orderCatalogMapping);
        } catch (Exception e) {
            logger.error("e", e);
        } finally {
            ThreadContext.remove(THREAD_LOCAL_VARIABLE_NO_NEED_WRITE_OPERATE_LOG);
        }
    }

    /**
     * 处理消费税
     *
     * @param order
     */
    public void dealTipsMapping(Order order) {
        logger.info("deal order {}", order.getId());
        //只有美国版本才处理消费税
        if (!Config.getInstance().isUsVersion()) {
            logger.info("not us version");
            return;
        }

        Double tipsFee = order.getTipsFee();
        //没有消费税
        if (getDouble(tipsFee) <= 0) {
            logger.info("has no tips tax");
            return;
        }

        //失效订单不处理
        if (getBoolean(order.getExpiredOrder())) {
            logger.info("expired order");
            return;
        }

        //已退款的不处理
        if (getBoolean(order.getReturnCash())) {
            logger.info("return cash");
            return;
        }

        //添加映射
        OrderCatalogMapping orderTipsMapping = new OrderCatalogMapping();
        orderTipsMapping.setShopId(order.getShopId());
        orderTipsMapping.setOrderId(order.getId());
        orderTipsMapping.setParentOrderId(order.getParentOrderId());
        orderTipsMapping.setCash(order.getTipsFee());
        orderTipsMapping.setCatalogId(ORDER_CATALOG_TIPS_ID);
        orderTipsMapping.setCatalogName_zh("小費");
        orderTipsMapping.setCatalogName_en("Tips");
        orderTipsMapping.setCatalogCount(1);

        ThreadContext.put(THREAD_LOCAL_VARIABLE_NO_NEED_WRITE_OPERATE_LOG, "true");
        try {
            orderTipsMapping.setCreateTime(order.getCreateTime());
            Long reportDataTime = getReportDataTime(order);
            order.setCreateTime(reportDataTime);
            commonService.createObject(orderTipsMapping);
        } catch (Exception e) {
            logger.error("e", e);
        } finally {
            ThreadContext.remove(THREAD_LOCAL_VARIABLE_NO_NEED_WRITE_OPERATE_LOG);
        }
    }

    /**
     * 处理支付类型
     *
     * @param order
     */
    public void dealOrderPayTypeMapping(Order order) {
        Long orderId = order.getId();
        Long shopId = order.getShopId();

        if (order.getPayType() == null) {
            logger.error("pay type is empty");
            return;
        }

        Integer payType = order.getPayType();

        try {
            OrderPayTypeMapping orderPayTypeMapping = new OrderPayTypeMapping();
            orderPayTypeMapping.setOrderId(orderId);
            orderPayTypeMapping.setShopId(shopId);
            orderPayTypeMapping.setParentOrderId(getParentOrderId(order));
            Long reportDataTime = getReportDataTime(order);
            orderPayTypeMapping.setCreateTime(reportDataTime);

            orderPayTypeMapping.setPayType(Long.valueOf(payType));
            PayType filterPayType = PayTypeService.getInstance().filterPayType(order.getShopId(), order.getPayType());
            orderPayTypeMapping.setPayTypeName_zh(filterPayType.getName_zh());
            orderPayTypeMapping.setPayTypeName_en(filterPayType.getName_en());

            Integer orderType = order.getOrderType();
            if (orderType == ORDER_TYPE_TANG_SHI || orderType == ORDER_TYPE_QUICK_FOOD) {
                orderPayTypeMapping.setOrderType(ORDER_REPORT_TYPE_TANG_SHI);
            } else if (orderType == ORDER_TYPE_TAKE_OUT) {
                orderPayTypeMapping.setOrderType(ORDER_REPORT_TYPE_TAKE_OUT);
            } else if (orderType == ORDER_REPORT_TYPE_TAKE_OUT_PLAT) {
                orderPayTypeMapping.setOrderType(ORDER_REPORT_TYPE_TAKE_OUT_PLAT);
            }

            orderPayTypeMapping.setCash(showMoneyDouble(order.getUserCost()));
            commonService.createObject(orderPayTypeMapping);

            //添加统计名称
            ShopSearchItemService.getInstance().addPayType(orderPayTypeMapping);
        } catch (Exception e) {
            logger.error("e", e);
        }
    }

    /**
     * 获取报表时间
     *
     * @param order
     * @return
     */
    private Long getReportDataTime(Order order) throws Exception {
        if (order.getParentOrderId() == null) {
            return order.getCreateTime();
        } else {
            //使用父订单的时间
            Order parentOrder = OrderService.getInstance().findOrder(order.getShopId(), order.getParentOrderId());
            if (parentOrder != null) {
                return parentOrder.getCreateTime();
            }
        }

        return null;
    }

    /**
     * 处理服务员与订单的映射
     *
     * @param order
     */
    public void dealOrderWaiterMapping(Order order) throws Exception {
        if (!OrderService.getInstance().isFoodTableOrder(order)) {
            logger.info("it is not the food table order");
            return;
        }

        //如果没有绑定服务员，也退出
        if (isEmpty(order.getWaiterName())) {
            logger.info("can not get the waiter name");
            return;
        }

        Long orderId = order.getId();
        Long shopId = order.getShopId();

        try {
            OrderWaiterMapping orderWaiterMapping = new OrderWaiterMapping();
            orderWaiterMapping.setShopId(shopId);
            orderWaiterMapping.setOrderId(orderId);
            orderWaiterMapping.setParentOrderId(getParentOrderId(order));
            orderWaiterMapping.setCash(showMoneyDouble(order.getUserCost() + showMoneyDouble(order.getRealDiscountFee())));
            orderWaiterMapping.setWaiterName(order.getWaiterName());
            orderWaiterMapping.setWaiterId(order.getWaiterId());
            orderWaiterMapping.setCreateTime(order.getCreateTime());
            Long reportDataTime = getReportDataTime(order);
            orderWaiterMapping.setCreateTime(reportDataTime);
            commonService.createObject(orderWaiterMapping);

            //添加统计名称
            ShopSearchItemService.getInstance().addWaiter(orderWaiterMapping);
        } catch (Exception e) {
            logger.error("e", e);
        }

    }

    /**
     * 获取父订单的ID
     *
     * @param order
     * @return
     */
    public Long getParentOrderId(Order order) {
        if (order.getParentOrderId() == null) {
            return order.getId();
        } else {
            return order.getParentOrderId();
        }
    }

    /**
     * 同步删除报表的数据
     *
     * @param shopId
     * @param orderId
     */
    public void deleteOrderReportData(Long shopId, Long orderId) throws Exception {
        logger.info("shop id is {}, order id {}", shopId, orderId);
        OrderWaiterMapping orderWaiterMapping = new OrderWaiterMapping();
        orderWaiterMapping.setShopId(shopId);
        orderWaiterMapping.setParentOrderId(orderId);
        orderWaiterMapping.setBeRequest(true);
        commonService.delete(orderWaiterMapping, false, null);

        OrderItemMapping orderItemMapping = new OrderItemMapping();
        orderItemMapping.setShopId(shopId);
        orderItemMapping.setParentOrderId(orderId);
        orderItemMapping.setBeRequest(true);
        commonService.delete(orderItemMapping, false, null);

        OrderCatalogMapping orderCatalogMapping = new OrderCatalogMapping();
        orderCatalogMapping.setShopId(shopId);
        orderCatalogMapping.setParentOrderId(orderId);
        orderCatalogMapping.setBeRequest(true);
        commonService.delete(orderCatalogMapping, false, null);

        OrderPayTypeMapping orderPayTypeMapping = new OrderPayTypeMapping();
        orderPayTypeMapping.setShopId(shopId);
        orderPayTypeMapping.setParentOrderId(orderId);
        orderPayTypeMapping.setBeRequest(true);
        commonService.delete(orderPayTypeMapping, false, null);

        OrderTypeMapping orderTypeMapping = new OrderTypeMapping();
        orderTypeMapping.setShopId(shopId);
        orderTypeMapping.setParentOrderId(orderId);
        orderTypeMapping.setBeRequest(true);
        commonService.delete(orderTypeMapping, false, null);

        OrderDiscountTicketMapping orderDiscountTicketMapping = new OrderDiscountTicketMapping();
        orderDiscountTicketMapping.setShopId(shopId);
        orderDiscountTicketMapping.setParentOrderId(orderId);
        orderDiscountTicketMapping.setBeRequest(true);
        commonService.delete(orderDiscountTicketMapping, false, null);

        //删除反馈，以及退款理由
        Feedback feedback = new Feedback();
        feedback.setShopId(shopId);
        feedback.setOrderId(orderId);
        feedback.setBeRequest(true);
        commonService.delete(feedback, false, null);

        //删除退款理由
        Reason reason = new Reason();
        reason.setShopId(shopId);
        reason.setOrderId(orderId);
        reason.setBeRequest(true);
        commonService.delete(reason, false, null);
    }

    /**
     * 查询分店
     *
     * @param shopId
     * @param shopList
     * @return
     * @throws Exception
     */
    public Shop findShop(Long shopId, List<Shop> shopList) throws Exception {
        List<Shop> filterList = shopList.stream().filter(p -> equals(p.getId(), shopId)).collect(Collectors.toList());
        if (isNotEmpty(filterList)) {
            return filterList.get(0);
        }

        Shop shop = findShopById(shopId);
        shopList.add(shop);
        return shop;
    }

    /**
     * 获取中文名
     *
     * @param shop
     * @return
     */
    public String getShopNameZh(Shop shop) {
        if (isNotEmpty(shop.getName_zh())) {
            return shop.getName_zh();
        }

        if (isNotEmpty(shop.getName())) {
            return shop.getName();
        }

        return shop.getName_en();
    }

    /**
     * 获取英文名
     *
     * @param shop
     * @return
     */
    public String getShopNameEn(Shop shop) {
        if (isNotEmpty(shop.getName_en())) {
            return shop.getName_en();
        }

        if (isNotEmpty(shop.getName())) {
            return shop.getName();
        }

        return shop.getName_zh();
    }

    /**
     * 获取分店名称
     *
     * @param shop
     * @return
     */
    public String getShopName(Shop shop) {
        int lang = getInt(shop.getLangInt());
        //双语言或者中文
        if (lang == 0 || lang == 1) {
            return getShopNameZh(shop);
        }

        return getShopNameEn(shop);
    }

    /**
     * 是否要打印小費
     *
     * @param shop
     * @return
     */
    public boolean canPrintTips(Shop shop) throws Exception {
        if (!Config.getInstance().isUsVersion()) {
            logger.info("is not us version");
            return false;
        }

        if (shop == null) {
            logger.info("shop is null");
            return false;
        }

        if (shop.getShopConfig() == null) {
            logger.info("shop config is null");
            return false;
        }

        boolean openTips = getBoolean(shop.getShopConfig().getTips());

        //开了一定要显示
        if (openTips) {
            return true;
        }

        //没开的时候，判断有没有小费，如果有小费，也要打印
        Double tipsFee = showDouble(ReportService.getInstance().sumTipsFee(shop.getId()));
        return tipsFee != 0d;
    }

    /**
     * 美国州的简写
     *
     * @param state
     * @return
     */
    public String getStateAbbreviation(String state) {
        return usState.getOrDefault(state, state);
    }

    public Map<String, String> getUsState() {
        return usState;
    }

    /**
     * 查询商铺的所有打印机
     *
     * @param shopId
     * @return
     */
    public List<Printer> findAllShopPrinters(Long shopId) {
        Printer printer = new Printer();
        printer.setShopId(shopId);
        printer.setEntity(true);
        return commonService.searchAll(printer);
    }

    /**
     * 判断foodpanda是否已经配置完成
     *
     * @param foodPandaConfig
     * @return
     */
    public boolean isFoodPandaConfigComplete(FoodPandaConfig foodPandaConfig) {
        return ClassUtil.getInstance().getAllField(foodPandaConfig).stream().filter(f -> {
            return ClassUtil.getInstance().isFieldString(f);
        }).allMatch(f -> {
            try {
                f.setAccessible(true);
                return isNotEmpty(f.get(foodPandaConfig));
            } catch (IllegalAccessException e) {
                logger.error("e", e);
                return false;
            }
        });
    }
}
